package com.nongxiange.unittesttutorial.redisson.delayedqueue;

import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * https://www.jianshu.com/p/f472af134ce0
 * https://blog.csdn.net/u012684638/article/details/103407931
 * 延时队列的过期机制是靠客户端的监听（定时任务+redis订阅）实现的，
 * 所以在消费数据的时候要先开启监听，监听的开启逻辑在redissonClient.getDelayedQueue()里面（会触发一次任务执行，并开启定时任务）
 *
 * 阻塞队列 List：KEY = queueName，执行 BLPOP 命令从左端弹出元素，右端插入元素。当一条数据到达过期时间的时候，会从redisson_delay_queue:{DelayMessage}中移除，加入到这个队列，客户端监听的就是这个队列，这个队列里面的全都是已经过期的数据。
 * 有序集合 Sorted Set：KEY = redisson_delay_queue_timeout:{queueName}，score 是元素的过期时间，按从小到大排序，过期时间小于当前时间表示已过期，删除集合中的元素，同时将普通集合 List中对应的元素删除，并将元素添加到阻塞队列 List等待客户端消费。
 * 普通集合 List：KEY = redisson_delay_queue:{DelayMessage}，按顺序从右端添加元素，元素过期会被删除。
 * 发布/订阅通道：redisson_delay_queue_channel，往延时队列中放入一个数据时，会将延时时间publish出去，客户端收到之后会按这个时间延时之后再执行定时任务。
 *
 *
 *
 * https://blog.csdn.net/m0_73311735/article/details/127070042
 */
public class RedisOutFromQueue {
	public static void main(String args[]) {
		Config config = new Config();
		config.useSingleServer().setAddress("redis://127.0.0.1:6379");
		RedissonClient redissonClient = Redisson.create(config);
		RBlockingQueue<Employer> blockingFairQueue = redissonClient.getBlockingQueue("zenddelay_queue");
		// 开启客户端监听（必须调用），否者系统重启时拿不到已过期数据，要等到系统第一次调用getDelayedQueue方法时才能开启监听
		RDelayedQueue<Employer> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
		while (true) {
			Employer callCdr = null;
			try {
				callCdr = blockingFairQueue.take();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("订单取消时间：" + new SimpleDateFormat("hh:mm:ss").format(new Date()) + "==订单生成时间" + callCdr.getPutTime());
		}
	}
}